#ifndef PUZZLE_HPP
# define PUZZLE_HPP

# define    UP      0
# define    RIGHT   1
# define    DOWN    2
# define    LEFT    3

# include <cmath>
# include <iostream>

# include "Coord.hpp"

class Puzzle
{
public:
    Puzzle();
    Puzzle(Puzzle const & p);
    Puzzle(unsigned int size, bool solved = false);
    virtual ~Puzzle();

    Puzzle& operator=(Puzzle const & p);
    bool operator==(Puzzle const & p) const;

    friend std::ostream& operator<<(std::ostream& s, Puzzle const & p);

public:
    bool copy(Puzzle const & p);
    void create(unsigned int size, bool solved = false);
    void display() const;
    void solve();
    bool move(int direction);

public:
    int getSize() const;
    int getAt(int i) const;
    int getAt(unsigned int x, unsigned int y) const;
    int getAt(Coord const & c) const;
    void setAt(int x, int y, int value);
    void setAt(Coord const & c, int value);
    size_t getHash() const;

private:
    size_t _doHash();
    void _resetPuzzle(int value = -1);
    bool _isEmptyNextCell(Coord const & current, int dirx, int diry);
    void _turnRight(int& dirx, int& diry);
    void _deletePuzzle();
    bool _getVoidCoord(Coord& c) const;

private:
    char* _puzzle;
    int _size;
};

inline std::ostream& operator<<(std::ostream& s, Puzzle const & p)
{
    if (p.getSize() == 0)
        s << "empty puzzle" << std::endl;
    else
    {
        for (int i = 0; i < p.getSize(); i++)
        {
            s << p.getAt(i) << " ";
            if ((i + 1) % static_cast<int>(sqrt(p.getSize())) == 0)
                s << std::endl;
        }
    }
    return s;
}
#endif // PUZZLE_HPP
